import numpy as np
import tensorflow as tf

# ①	使用GradintTape计算一元二次方程的最小值
# 1)	构造函数，a=4,b=-4,c=4
x = tf.Variable(0., dtype=tf.float32, name='x')
a = tf.constant(4., dtype=tf.float32, name='a')
b = tf.constant(-4., dtype=tf.float32, name='b')
c = tf.constant(4., dtype=tf.float32, name='c')


@tf.function
def xmodel(x):
    y = a * x ** 2 + b * x + c
    return y


# 2)	使用GradintTape计算x的一阶导数
@tf.function
def xstep(x):
    with tf.GradientTape() as tape:
        y = xmodel(x)
    dydx = tape.gradient(y, x)
    x.assign_sub(alpha * dydx)
    return y


# 3)	使用随机梯度下降更新导数，自行选择学习率和循环次数
alpha = 0.01
iters = 200
group = int(np.ceil(iters / 10))
x_last = x.numpy()
for i in range(iters):
    y = xstep(x)
    x_this = x.numpy()
    if np.isclose(x_last, x_this):
        print('Converged!')
        break
    x_last = x_this
    if i % group == 0:
        print(f'#{i + 1}: x = {x_this}, y = {y}')
if i % group != 0:
    print(f'#{i + 1}: x = {x_this}, y = {y}')

# 4)	输出该一元二次方程的最小值及对应的x值
print(f'该一元二次方程的最小值: {y}, 及对应的x值: {x_this}')
xmin = - b / (2 * a)
ymin = xmodel(xmin)
print(f'该一元二次方程的理论最小值: {ymin}, 及对应的x值: {xmin}')
